/*
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  Copyright (c) 2003-2009 PRO ACTIVE SAS - www.proactive-group.com
   
  Ref_Mifare.c
  ------------

  This is the reference application that shows how to read/write data
  in a Philips MIFARE classic tag.
  
  JDA 21/11/2003 : initial release
  JDA 03/11/2006 : calling SPROX_A_SelectIdle and SPROX_A_Halt instead
                   of Mf500xxxx functions
  JDA 10/01/2007 : fixed a few warnings when compiling under GCC
  JDA 23/02/2007 : added the '-b' flag for "benchmarking"

*/
#include "products/springprox/springprox.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

BYTE key_ff[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
BYTE key_cc[6] = { 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC };

SWORD reader_load_test_keys(void);
SWORD reader_load_default_keys(void);
SWORD reader_erase_keys(void);

SWORD card_format_transport(BYTE sect_count, BOOL persist);
SWORD card_format_for_test(BYTE sect_count, BOOL persist);

SWORD card_read(BYTE sect_count);
SWORD card_read_failure(BYTE sect_count);
SWORD card_read_test_00(BYTE sect_count, BOOL transport);
SWORD card_write_test_xx(BYTE sect_count);
SWORD card_read_test_xx(BYTE sect_count);

BOOL bench = FALSE;
BOOL test = FALSE;

int main(int argc, char **argv)
{
  SWORD rc;
  int i;
  char s_buffer[64];
  BYTE atq[2];
  BYTE sak[1];
  BYTE uid[12];
  BYTE uid_len = 12;
  BYTE sect_count = 0;

  char *device = NULL;
  
  i = 1;
  if ((argc > i) && !strcmp(argv[i], "-b"))
  {
    bench = TRUE;
    i++;
  }
  if ((argc > i) && !strcmp(argv[i], "-t"))
  {
    test = TRUE;
    i++;
  }

  if (argc > i)
    device = argv[i];

  printf("SpringCard SDK for K531/SpringProx-CF/CSB Legacy\n");
  printf("\n");
  printf("NXP MIFARE 'CLASSIC' (1k and 4k) reference demo\n");
  printf("-----------------------------------------------\n");
  printf("www.springcard.com\n\n");

  rc = SPROX_GetLibrary(s_buffer, sizeof(s_buffer));
  if (rc != MI_OK)
  {
    printf("Failed to get API version\n");
    goto done;
  }
  printf("API version : %s\n", s_buffer);
  printf("\n");

  /* Open reader */
  /* ----------- */

  rc = SPROX_ReaderOpen(device);
  if (rc != MI_OK)
  {
    printf("Reader not found\n");
    goto done;
  }

  rc = SPROX_ReaderGetDevice(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("Reader found on %s\n", s_buffer);

  rc = SPROX_ReaderGetFirmware(s_buffer, sizeof(s_buffer));
  if (rc == MI_OK)
    printf("Reader firwmare is %s\n", s_buffer);

  /* Configure reader in ISO 14443-A mode */
  /* ------------------------------------ */
  rc = SPROX_SetConfig(CFG_MODE_ISO_14443_A);
  if (rc != MI_OK)
  {
    printf("Failed to configure the reader\n");
    goto close;
  }

  /* RF field ON */
  SPROX_ControlRF(TRUE);

  /* Select any tag in the ISO 14443-A RF field */
  /* ------------------------------------------ */
  rc = SPROX_A_SelectIdle(atq, uid, &uid_len, sak);
  if (rc != MI_OK)
  {
    printf("No available tag in RF field\n");
    goto close;
  }

  printf("Tag found: UID=");
  for (i = 0; i < uid_len; i++)
    printf("%02X", uid[i]);
  printf(" ATQ=%02X%02X SAK=%02X\n", atq[1], atq[0], sak[0]);

  if ((atq[1] != 0x00)
      || ((atq[0] != 0x02) && (atq[0] != 0x04) && (atq[0] != 0x18)))
  {
    printf("This is not a Mifare classic tag\n");
    goto tag_halt;
  }

  if ((sak[0] & 0x1F) == 0x08)
  {
    /* Mifare classic 1k : 16 sectors, 3+1 blocks in each sector */
    printf("Tag appears to be a Mifare classic 1k\n");
    sect_count = 16;
  } else if ((sak[0] & 0x1F) == 0x18)
  {
    /* Mifare classic 4k : 40 sectors, 3+1 blocks in 32-first sectors, 15+1 blocks in the 8-last sectors */
    printf("Tag appears to be a Mifare classic 4k\n");
    sect_count = 40;
  }
  
  if (test)
  {
    printf("Resetting the card, restoring channel...\n");
    rc = SPROX_A_Halt();
    if (rc != MI_OK) goto close;
    rc = SPROX_A_SelectAgain(uid, uid_len);
    if (rc != MI_OK) goto close;

    printf("Preparing the card for the test...\n");
    rc = card_format_transport(sect_count, TRUE);
    if (rc != MI_OK) goto close;
    rc = card_format_for_test(sect_count, TRUE);
    if (rc != MI_OK) goto close;

    printf("Loading the test keys into the reader...\n");
    rc = reader_load_test_keys();
    if (rc != MI_OK) goto close;

    printf("Reading the card...\n");
    rc = card_read_test_00(sect_count, FALSE);
    if (rc != MI_OK) goto close;

    printf("Writing some data into the card...\n");
    rc = card_write_test_xx(sect_count);
    if (rc != MI_OK) goto close;

    printf("Reading the card again...\n");
    rc = card_read_test_xx(sect_count);
    if (rc != MI_OK) goto close;

    printf("Erasing the test keys from the reader...\n");
    rc = reader_erase_keys();
    if (rc != MI_OK) goto close;

    printf("Trying to read the card again...\n");
    rc = card_read_failure(sect_count);
    if (rc != MI_OK) goto close;

    printf("Erasing the card, back to transport conditions...\n");
    rc = card_format_transport(sect_count, TRUE);
    if (rc != MI_OK) goto close;

    printf("Resetting the card, restoring channel...\n");
    rc = SPROX_A_Halt();
    if (rc != MI_OK) goto close;
    rc = SPROX_A_SelectAgain(uid, uid_len);
    if (rc != MI_OK) goto close;

    printf("Reading the card again...\n");
    rc = card_read_failure(sect_count);
    if (rc != MI_OK) goto close;

    printf("Loading the default keys into the reader...\n");
    rc = reader_load_default_keys();
    if (rc != MI_OK) goto close;

    printf("Reading the card (last !)...\n");
    rc = card_read_test_00(sect_count, TRUE   );
    if (rc != MI_OK) goto close;

    printf("All tests passed OK.\n");


  } else
  {
    rc = card_read(sect_count);
  }

  goto tag_halt;

tag_halt:
  /* Halt the tag */
  rc = SPROX_A_Halt();
  if (rc != MI_OK)
  {
    printf("Failed to halt the tag\n");
    goto close;
  }

close:
  /* Close the reader */
  SPROX_ControlRF(FALSE);
  SPROX_ReaderClose();

done:
  /* Display last error */
  if (rc == MI_OK)
  {
    printf("Done\n");
  } else
  {
    printf("%s (%d)\n", SPROX_GetErrorMessage(rc), rc);
  }
  return 0;
}


SWORD card_read(BYTE sect_count)
{
  BYTE data[240];
  clock_t t0, t1;
  BYTE bloc_count, bloc, sect;
  BYTE offset;
  SWORD rc;

  if (bench)
  {
    printf("Reading %d sectors...\n", sect_count);
    t0 = clock();
  }

  bloc = 0;
  for (sect = 0; sect < sect_count; sect++)
  {
    if (!bench)
      printf("Reading sector %02d : ", sect);

    /* The SPROX_MifStReadSector functions assumes that the data buffer is big enough... */
    rc = SPROX_MifStReadSector(NULL, sect, data, NULL);
    if (rc != MI_OK)
    {
      if (bench)
        printf("Read sector %02d ", sect);
      printf("[Failed]\n");
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }
    
    if (!bench)
    {
      printf("[OK]\n");
      /* Display sector's data */
      if (sect < 32)
        bloc_count = 3;
      else
        bloc_count = 15;
      for (bloc = 0; bloc < bloc_count; bloc++)
      {
        printf("%02d : ", bloc);
        /* Each blocks is 16-bytes wide */
        for (offset = 0; offset < 16; offset++)
        {
          printf("%02X ", data[16 * bloc + offset]);
        }
        for (offset = 0; offset < 16; offset++)
        {
          if (data[16 * bloc + offset] >= ' ')
          {
            printf("%c", data[16 * bloc + offset]);
          } else
          {
            printf(".");
          }
        }
        printf("\n");
      }
    }
  }

  if (bench)
  {
    t1 = clock(); 
    printf("Time elapsed: %ldms\n", (t1 - t0) / (CLOCKS_PER_SEC/1000));
  }

  return MI_OK;
}

void compute_test_keys(BYTE sect, BYTE key_a[6], BYTE key_b[6])
{
  char str[12];

  if (key_a != NULL)
  {
    sprintf(str, "SC_A%02d", sect);
    memcpy(key_a, str, 6);
  }
  if (key_b != NULL)
  {
    sprintf(str, "SC_A%02d", sect);
    memcpy(key_b, str, 6);
  }
}

SWORD card_format_transport(BYTE sect_count, BOOL persist)
{
  BYTE sect;
  BYTE key_a[6], key_b[6];
  BYTE data[240];
  SWORD rc;

  memset(data, 0, sizeof(data));

  for (sect = 0; sect < sect_count; sect++)
  {
    compute_test_keys(sect, key_a, key_b);

    rc = SPROX_MifStUpdateAccessBlock(NULL, sect, key_b, key_ff, key_ff, ACC_BLOCK_TRANSPORT, ACC_BLOCK_TRANSPORT, ACC_BLOCK_TRANSPORT, ACC_AUTH_TRANSPORT);
    if (rc != MI_OK)
      rc = SPROX_MifStUpdateAccessBlock(NULL, sect, key_ff, key_ff, key_ff, ACC_BLOCK_TRANSPORT, ACC_BLOCK_TRANSPORT, ACC_BLOCK_TRANSPORT, ACC_AUTH_TRANSPORT);
    if (!persist && (rc != MI_OK))
    {
      printf("Format (transport) sector %02d [Failed]\n", sect);
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }

    rc = SPROX_MifStWriteSector(NULL, sect, data, key_ff);
    if (!persist && (rc != MI_OK))
    {
      printf("Write (transport) sector %02d [Failed]\n", sect);
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }
  }

  rc = SPROX_MifStReadBlock(NULL, 0, data, key_cc);
  if (rc != MI_AUTHERR)
  {
    printf("Read sector %02d -> %d\n", sect, rc);
    return 1;
  }

  return MI_OK;
}

SWORD card_format_for_test(BYTE sect_count, BOOL persist)
{
  BYTE sect;
  BYTE key_a[6], key_b[6];
  BYTE data[240];
  SWORD rc;

  memset(data, 0, sizeof(data));

  for (sect = 0; sect < sect_count; sect++)
  {
    compute_test_keys(sect, key_a, key_b);

    rc = SPROX_MifStUpdateAccessBlock(NULL, sect, key_ff, key_a, key_b, ACC_BLOCK_VALUE, ACC_BLOCK_VALUE, ACC_BLOCK_VALUE, ACC_AUTH_NORMAL);
    if (rc != MI_OK)
      rc = SPROX_MifStUpdateAccessBlock(NULL, sect, key_b, key_a, key_b, ACC_BLOCK_VALUE, ACC_BLOCK_VALUE, ACC_BLOCK_VALUE, ACC_AUTH_NORMAL);
    if (!persist && (rc != MI_OK))
    {
      printf("Format (for test) sector %02d [Failed]\n", sect);
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }

    rc = SPROX_MifStWriteSector(NULL, sect, data, key_b);
    if (!persist && (rc != MI_OK))
    {
      printf("Write (for test) sector %02d [Failed]\n", sect);
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }
  }

  rc = SPROX_MifStReadBlock(NULL, 0, data, key_cc);
  if (rc != MI_AUTHERR)
  {
    printf("Read sector %02d -> %d\n", sect, rc);
    return 1;
  }

  return MI_OK;
}

SWORD reader_load_test_keys(void)
{
  BYTE idx;
  BYTE key_a[6], key_b[6];
  SWORD rc;

  for (idx=0; idx<16; idx++)
  {
    compute_test_keys(idx, key_a, key_b);

    rc = SPROX_MifLoadKey(TRUE, FALSE, idx, key_a);
    if (rc != MI_OK)
    {
      printf("Load key A%02d to EEPROM [Failed]\n", idx);
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }

    rc = SPROX_MifLoadKey(TRUE, TRUE, idx, key_b);
    if (rc != MI_OK)
    {
      printf("Load key B%02d to EEPROM [Failed]\n", idx);
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }
  }

  return MI_OK;
}

SWORD reader_load_default_keys(void)
{
  static const BYTE KEYS[3][6] = { { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 },
                                   { 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5 },
                                   { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 } };

  BYTE idx;
  BYTE key_v[6];
  SWORD rc;

  /* Base key A */
  memcpy(key_v, KEYS[0], 6);
  rc = SPROX_MifLoadKey(TRUE, FALSE, 0, key_v);
  if (rc != MI_OK) goto failed;

  /* Base key B */
  memcpy(key_v, KEYS[1], 6);
  rc = SPROX_MifLoadKey(TRUE, TRUE, 0, key_v);
  if (rc != MI_OK) goto failed;

  /* Transport key FF */
  memset(key_v, 0xFF, 6);
  rc = SPROX_MifLoadKey(TRUE, FALSE, 1, key_v);
  if (rc != MI_OK) goto failed;
  rc = SPROX_MifLoadKey(TRUE, TRUE, 1, key_v);
  if (rc != MI_OK) goto failed;

  /* NFC key */
  memcpy(key_v, KEYS[2], 6);
  rc = SPROX_MifLoadKey(TRUE, FALSE, 2, key_v);
  if (rc != MI_OK) goto failed;
  rc = SPROX_MifLoadKey(TRUE, TRUE, 2, key_v);
  if (rc != MI_OK) goto failed;

  /* Blank key */
  memset(key_v, 0x00, 6);
  rc = SPROX_MifLoadKey(TRUE, FALSE, 3, key_v);
  if (rc != MI_OK) goto failed;
  rc = SPROX_MifLoadKey(TRUE, TRUE, 3, key_v);
  if (rc != MI_OK) goto failed;
  
  /* Dummy keys */
  memset(key_v, 0xCC, 6);
  for (idx=4; idx<16; idx++)
  {
    rc = SPROX_MifLoadKey(TRUE, FALSE, idx, key_v);
    if (rc != MI_OK) goto failed;
    rc = SPROX_MifLoadKey(TRUE, TRUE, idx, key_v);
    if (rc != MI_OK) goto failed;
  }

  return MI_OK;

failed:
  printf("Load key to EEPROM [Failed]\n");
  printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
  return rc;
}


SWORD reader_erase_keys(void)
{
  BYTE idx;
  BYTE key_cc[6];
  SWORD rc;

  memset(key_cc, 0xCC, 6);

  for (idx=0; idx<16; idx++)
  {
    rc = SPROX_MifLoadKey(TRUE, FALSE, idx, key_cc);
    if (rc != MI_OK)
    {
      printf("Load key A%02d to EEPROM [Failed]\n", idx);
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }

    rc = SPROX_MifLoadKey(TRUE, TRUE, idx, key_cc);
    if (rc != MI_OK)
    {
      printf("Load key B%02d to EEPROM [Failed]\n", idx);
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }
  }

  return MI_OK;
}

SWORD card_read_failure(BYTE sect_count)
{
  BYTE data[240];
  BYTE sect;
  SWORD rc;

  for (sect = 0; sect < 16; sect++)
  {
    rc = SPROX_MifStReadSector(NULL, sect, data, NULL);
    if (rc == MI_OK)
    {
      printf("Read sector %02d is OK, but it shouldn't!!!", sect);
      return 1;
    }
    if (rc != MI_AUTHERR)
    {
      printf("Read sector %02d ", sect);
      printf("[Failed]\n");
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }
  }

  return MI_OK;
}

SWORD card_read_test_00(BYTE sect_count, BOOL transport)
{
  BYTE data[240];
  BYTE key_a[6];
  BYTE sect;
  BYTE offset, length;
  SWORD rc;

  for (sect = 0; sect < sect_count; sect++)
  {
    memset(data, 0xCC, sizeof(data));

    if (!transport && (sect >= 16))
    {
      compute_test_keys(sect, key_a, NULL);
      rc = SPROX_MifStReadSector(NULL, sect, data, key_a);
    } else
    {
      rc = SPROX_MifStReadSector(NULL, sect, data, NULL);
    }
    if (rc != MI_OK)
    {
      printf("Read sector %02d ", sect);
      printf("[Failed]\n");
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }

    if (sect < 32) length = 48; else length = 240;
    if (sect == 0) offset = 16; else offset = 0;

    for (; offset<length; offset++)
    {
      if (data[offset] != 0x00)
      {
        printf("Sector %02d, expecting only 00, found %02X at offset %d\n", sect, data[offset], offset);
        return 1;
      }
    }
  }

  rc = SPROX_MifStReadBlock(NULL, 0, data, key_cc);
  if (rc != MI_AUTHERR)
  {
    printf("Read sector %02d -> %d\n", sect, rc);
    return 1;
  }

  return MI_OK;
}

SWORD card_read_test_xx(BYTE sect_count)
{
  BYTE data[240];
  BYTE key_a[6];
  BYTE sect;
  BYTE offset, length;
  SWORD rc;

  for (sect = 0; sect < sect_count; sect++)
  {
    memset(data, 0xCC, sizeof(data));

    if (sect >= 16)
    {
      compute_test_keys(sect, key_a, NULL);
      rc = SPROX_MifStReadSector(NULL, sect, data, key_a);
    } else
    {
      rc = SPROX_MifStReadSector(NULL, sect, data, NULL);
    }
    if (rc != MI_OK)
    {
      printf("Read sector %02d ", sect);
      printf("[Failed]\n");
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }

    if (sect < 32) length = 48; else length = 240;
    if (sect == 0) offset = 16; else offset = 0;

    for (; offset<length; offset++)
    {
      BYTE v = 0x80 ^ sect_count ^ offset;
      if (data[offset] != v)
      {
        printf("Sector %02d, found %02X instead of %02X at offset %d\n", sect, data[offset], v, offset);
        return 1;
      }
    }
  }

  rc = SPROX_MifStReadBlock(NULL, 0, data, key_cc);
  if (rc != MI_AUTHERR)
  {
    printf("Read sector %02d -> %d\n", sect, rc);
    return 1;
  }

  return MI_OK;
}

SWORD card_write_test_xx(BYTE sect_count)
{
  BYTE data[240];
  BYTE key_b[6];
  BYTE sect;
  BYTE offset;
  SWORD rc;

  for (sect = 0; sect < sect_count; sect++)
  {
    for (offset=0; offset<240; offset++)
      data[offset] = 0x80 ^ sect_count ^ offset;

    if (sect >= 16)
    {
      compute_test_keys(sect, NULL, key_b);
      rc = SPROX_MifStWriteSector(NULL, sect, data, key_b);
    } else
    {
      rc = SPROX_MifStWriteSector(NULL, sect, data, NULL);
    }
    if (rc != MI_OK)
    {
      printf("Write sector %02d ", sect);
      printf("[Failed]\n");
      printf("  %s (%d)\n", SPROX_GetErrorMessage(rc), rc);
      return rc;
    }
  }

  rc = SPROX_MifStReadBlock(NULL, 0, data, key_cc);
  if (rc != MI_AUTHERR)
  {
    printf("Read sector %02d -> %d\n", sect, rc);
    return 1;
  }

  return MI_OK;
}

